package com.bizmda.bizsip.app.config;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.io.resource.NoResourceException;
import com.bizmda.bizsip.app.executor.*;
import com.bizmda.bizsip.common.BizException;
import com.bizmda.bizsip.common.BizResultEnum;
import lombok.extern.slf4j.Slf4j;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 史正烨
 */
@Slf4j
public class AppServiceMapping {
    private Map<String, AbstractAppExecutor> integratorExecutorMap;
    private String configPath;

    private static final Map<String, Object> SERVICE_SCRIPT_SUFFIX_MAP = new HashMap<>();

    static {
        SERVICE_SCRIPT_SUFFIX_MAP.put("script", ScriptAppExecutor.class);
    }

    public AppServiceMapping(String configPath) throws BizException {
        this.configPath = configPath;
        this.loadAppServiceFromServiceDir();
        this.loadAppServiceFromServiceYml();
    }

    public void loadAppServiceFromServiceDir() throws BizException {
        String scriptPath = null;
        this.integratorExecutorMap = new HashMap<>(16);
        List<File> files = new ArrayList<>();
        if (this.configPath == null) {
            try {
                log.info("application.yml中没有配置bizsip.config-path，从ClassPath资源/service/*中读取App服务配置文件");
                ClassPathResource resource = new ClassPathResource("/service");
                File fileDir = resource.getFile();
                scriptPath = fileDir.getPath();
                if (fileDir.exists() && fileDir.isDirectory()) {
                    files = FileUtil.loopFiles(fileDir);
                }
            } catch (NoResourceException e) {
                log.warn("资源不存在:/service");
            }
        } else {
            if (this.configPath.endsWith("/")) {
                scriptPath = this.configPath + "service";
            } else {
                scriptPath = this.configPath + "/service";
            }
            log.info("从{}目录中读取App服务配置文件",scriptPath);
            files = FileUtil.loopFiles(scriptPath);
        }
        String suffix;
        AbstractAppExecutor appExecutor = null;

        for (File file : files) {
            suffix = FileUtil.getSuffix(file).toLowerCase();
            log.info("处理App服务配置文件: {}",FileUtil.getAbsolutePath(file));
            Class<AbstractAppExecutor> integratorClazz = (Class<AbstractAppExecutor>) SERVICE_SCRIPT_SUFFIX_MAP.get(suffix);
            if (integratorClazz != null) {
                FileReader fileReader = new FileReader(file);
                String fileContent = fileReader.readString();
                String allPath = FileUtil.normalize(file.getPath());
                String serviceId = allPath.substring(scriptPath.length(), allPath.length() - suffix.length() - 1);
                log.info("装载script类型App服务[{}]: {}",serviceId,suffix);
                try {
                    Constructor<AbstractAppExecutor> constructor = integratorClazz.getDeclaredConstructor(String.class, String.class, Map.class);
                    Map configMap = new HashMap(16);
                    configMap.put("script", fileContent);
                    appExecutor = constructor.newInstance(serviceId, suffix, configMap);
                } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                    throw new BizException(BizResultEnum.APP_SERVICE_CLASS_LOAD_ERROR, e);
                }
                appExecutor.init();
                integratorExecutorMap.put(serviceId, appExecutor);
            }
        }
    }

    private void loadAppServiceFromServiceYml() throws BizException {
        Yaml yaml = new Yaml();
        List<Map<String, Object>> appServiceList = null;
        appServiceList = this.getAppServiceList(yaml);
        String appServiceId;
        for (Map<String, Object> serviceConfigMap : appServiceList) {
            if (this.isAppYml) {
                appServiceId = (String) serviceConfigMap.get("app-service-id");
            }
            else {
                appServiceId = (String) serviceConfigMap.get("bizServiceId");
            }
            String type = (String) serviceConfigMap.get("type");
            log.info("装载App服务[{}]: type={}",appServiceId,type);
            switch (type) {
                case "bean-service":
                    BeanAppExecutor beanIntegratorExecutor = new BeanAppExecutor(appServiceId, type, serviceConfigMap,this.isAppYml);
                    beanIntegratorExecutor.init();
                    this.integratorExecutorMap.put(appServiceId, beanIntegratorExecutor);
                    break;
                case "app-bean-service":
                    AppBeanAppExecutor appBeanIntegratorExecutor = new AppBeanAppExecutor(appServiceId, type, serviceConfigMap,this.isAppYml);
                    appBeanIntegratorExecutor.init();
                    this.integratorExecutorMap.put(appServiceId, appBeanIntegratorExecutor);
                    break;
//                case "integrator-bean-service":
//                    IntegratorBeanAppExecutor integratorBeanIntegratorExecutor = new IntegratorBeanAppExecutor(appServiceId, type, serviceConfigMap,this.isAppYml);
//                    integratorBeanIntegratorExecutor.init();
//                    this.integratorExecutorMap.put(appServiceId, integratorBeanIntegratorExecutor);
//                    break;
                case "sink-service":
                    SinkServiceAppExecutor serviceIntegratorExecutor = new SinkServiceAppExecutor(appServiceId, type, serviceConfigMap,this.isAppYml);
                    serviceIntegratorExecutor.init();
                    this.integratorExecutorMap.put(appServiceId, serviceIntegratorExecutor);
                    break;
                default:
                    log.error("装载出错，未知服务类型:" + type);
            }
        }
    }

    private boolean isAppYml = true;
    private List<Map<String, Object>> getAppServiceList(Yaml yaml) throws BizException {
        List<Map<String, Object>> appServiceList;
        ClassPathResource resource;
        try {
            if (this.configPath == null) {
                resource = new ClassPathResource("/app.yml");
                appServiceList = (List<Map<String, Object>>) yaml.load(new FileInputStream(resource.getFile()));
            } else {
                appServiceList = (List<Map<String, Object>>) yaml.load(new FileInputStream(new File(this.configPath + "/app.yml")));
            }
            this.isAppYml = true;
            return appServiceList;
        } catch (FileNotFoundException e) {
//            throw new BizException(BizResultEnum.OTHER_FILE_NOTFOUND, "app.yml");
            log.warn("没有找到app.yml，建议把原有service.yml升级到app.yml!");
        }
        try {
            if (this.configPath == null) {
                resource = new ClassPathResource("/service.yml");
                appServiceList = (List<Map<String, Object>>) yaml.load(new FileInputStream(resource.getFile()));
            } else {
                appServiceList = (List<Map<String, Object>>) yaml.load(new FileInputStream(new File(this.configPath + "/service.yml")));
            }
            this.isAppYml = false;
            return appServiceList;
        } catch (FileNotFoundException e) {
            throw new BizException(BizResultEnum.OTHER_FILE_NOTFOUND, "app.yml、service.yml都没在配置目录中找到!");
        }
    }

    public AbstractAppExecutor getAppExecutor(String bizServiceId) {
//        if (!this.isSpringBeanLoad) {
//            this.isSpringBeanLoad = true;
////            this.loadBizServiceBeanService();
//            try {
//                this.loadAppServiceFromServiceYml();
//            } catch (BizException e) {
//                log.error("装载service.yml出错", e);
//            }
//        }

        AbstractAppExecutor abstractAppExecutor = this.integratorExecutorMap.get(bizServiceId);

        return abstractAppExecutor;
    }
}
